#include <random>
#include <algorithm>
#include "msli/fileIO/FileIO.hpp"
#include "msli/index/HuffmanTree.hpp"
#include "msli/index/PMLITree.hpp"
#include "msli/common/CalCirclePos.h"
#include <set>

int main() {
    std::chrono::steady_clock::time_point time_begin;
    std::chrono::steady_clock::time_point time_end;

    std::chrono::steady_clock::time_point time_begin1;
    std::chrono::steady_clock::time_point time_end1;
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
    string filename_main = "../../results/msli/main_out.txt";
    std::ofstream outFile(filename_main);
    if (outFile.is_open()) {
        HuffmanTree<Key_TYPE> huffmanTree;
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        string filename_base_dataset = "../../dataset/";
        string filename_fft_id = filename_base_dataset + "points_fft_id.txt";
        string filename_fft_noId = filename_base_dataset + "points_fft.txt";
        string filename_original = filename_base_dataset + "points_original.txt";

//        string filename_fft_id = filename_base_dataset + "forest_fft_id_100000.txt";
//        string filename_fft_noId = filename_base_dataset + "forest_fft_100000.txt";
//        string filename_original = filename_base_dataset + "forest_original_100000.txt";
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        // 从文件中读取数据并加工
//    std::vector<Point<Key_TYPE>> points_temp;
//    ReadDataFromFileWithID(filename_fft_id, points_temp);
//    WritePointsToFileWithNotOid(filename_fft_noId, points_temp);

        std::vector<Point<Key_TYPE>> points;
//    ReadDataFromFileWithNotOid(filename_original, points);
        ReadDataFromFileWithID(filename_fft_id, points);
        uint64_t DATASIZE = points.size();
        outFile << "*************************************************************************************" << std::endl;
        outFile << "数据集大小: " << DATASIZE << "\t最后一行数据点的 ID: " << points[DATASIZE - 1].id << std::endl;

//    WritePointsToFileWithNotOidByNums("../../dataset/forest_original_100000.txt",points,100000);
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        time_begin = std::chrono::steady_clock::now();
        huffmanTree.build(points, DATASIZE);
        time_end = std::chrono::steady_clock::now();
        outFile << "*************************************************************************************" << std::endl;
        outFile << "哈夫曼树构建时间:"
                << std::chrono::duration_cast<std::chrono::milliseconds>(time_end - time_begin).count() << " [µs]"
                << std::endl;
        outFile << "*************************************************************************************" << std::endl;
        outFile << "树的高度: " << huffmanTree.getHeight() << std::endl;
        outFile << "*************************************************************************************" << std::endl;
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        TwoLayerIndex<Key_TYPE> twoLayerIndex;
        std::vector<TempNode<Key_TYPE>> results;

        // 从上往下收集符合条件的内节点：保证收集的结果覆盖原数据集
        if (huffmanTree.collectAndSortNodes(results, LEAF_MUN_MIN, LEAF_MUN_MAX, DATASIZE, outFile)) {
            time_begin = std::chrono::steady_clock::now();
            twoLayerIndex.buildTwoLayerIndex(huffmanTree, results);
            time_end = std::chrono::steady_clock::now();

            outFile << "两层索引构建时间【暂时包含IO输出时间】:"
                    << std::chrono::duration_cast<std::chrono::milliseconds>(time_end - time_begin).count()
                    << " [µs]" << std::endl;
            outFile << "*********************************************************************************" << std::endl;
            twoLayerIndex.printUpperNodesInTableFormat(outFile);
            twoLayerIndex.printBottomNodesInTableFormat();
        }
        outFile << "*************************************************************************************" << std::endl;
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        std::set<Point<Key_TYPE>> bottomPoints;
        std::vector<Point<Key_TYPE>> vecBottomPoints;
        twoLayerIndex.getBottomPointsSize(bottomPoints, vecBottomPoints);
        if (bottomPoints.size() >= DATASIZE) {
            outFile << "实际点数: 【<" << bottomPoints.size() << "," << vecBottomPoints.size() << ">】不存在漏点问题 \t"
                    << "哈夫曼树根节点覆盖元素个数: " << huffmanTree.getRoot()->leafCount << "; 根节点半径: "
                    << huffmanTree.getRoot()->radius << std::endl;
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
            // range query
            std::vector<Point<Key_TYPE>> list_rangeQry;
            string query_filename = filename_base_dataset+"query_points.txt";
            ReadDataFromFile_Query(query_filename, list_rangeQry);

            double query_radius = 0.2;
            double time = 0.0;

            for (auto &queryPoint: list_rangeQry) {       // 遍历每个查询点

                // 纯扫描结果
                std::set<OID_TYPE> rangeAnsByScan;
                time_begin = std::chrono::steady_clock::now();
                for (auto &point: points) {
                    DIS_TYPE dist = queryPoint.distance(point);
                    if (dist <= query_radius) {
                        rangeAnsByScan.insert(point.id);
                    }
                }
                time_end = chrono::steady_clock::now();
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
                std::cout << "************************************************************************" << std::endl;
                std::cout << "该轮【纯扫描数据集】查询结果数:【" << rangeAnsByScan.size() << "】; 该轮【纯扫描数据集】查询耗时: "
                          << chrono::duration_cast<chrono::microseconds>(time_end - time_begin).count() << " [µs]"
                          << endl;
                std::cout << "************************************************************************" << std::endl;
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
                outFile << "*****************************************************************************" << std::endl;
                outFile << "该轮【纯扫描数据集】查询结果数:【" << rangeAnsByScan.size() << "】; 该轮【纯扫描数据集】查询耗时: "
                        << chrono::duration_cast<chrono::microseconds>(time_end - time_begin).count() << " [µs]"
                        << endl;
                outFile << "*****************************************************************************" << std::endl;
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

                // 无枢轴点纯扫描底层簇结果
                std::set<OID_TYPE> scanBottomSet;
                std::set<OID_TYPE> scanCluSet;
                time_begin = std::chrono::steady_clock::now();
                twoLayerIndex.ScanBottom(queryPoint, query_radius, scanBottomSet, scanCluSet);
                time_end = chrono::steady_clock::now();
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
                outFile << "*****************************************************************************" << std::endl;
                outFile << "该轮【纯扫描底层簇】查询结果数: 【" << scanBottomSet.size() << "】;  该轮【无枢轴点纯扫描底层簇】查询耗时: "
                        << chrono::duration_cast<chrono::microseconds>(time_end - time_begin).count() << " [µs]"
                        << endl;

                outFile << "【无枢轴点纯扫描】底层簇结果 【所属底层簇 ID】: " << "总数: <" << scanCluSet.size() << ">\t";
                for (auto &element: scanCluSet) {
                    outFile << element << " ";
                }
                outFile << std::endl;
                outFile << "*****************************************************************************" << std::endl;
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
                std::cout << "*************************************************************************" << std::endl;
                std::cout << "该轮【纯扫描底层簇】查询结果数: 【" << scanBottomSet.size() << "】;  该轮【无枢轴点纯扫描底层簇】查询耗时: "
                          << chrono::duration_cast<chrono::microseconds>(time_end - time_begin).count() << " [µs]"
                          << endl;

//                std::cout << "【无枢轴点纯扫描】底层簇结果 【所属底层簇 ID】: " << "总数: <" << scanCluSet.size() << ">\t";
//                for (auto &element: scanCluSet) {
//                    std::cout << element << " ";
//                }
//                std::cout << std::endl;
                scanCluSet.clear();
                std::cout << "*************************************************************************" << std::endl;
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
                std::vector<InterSecVector<Key_TYPE>> PerRangeQuery;
                std::vector<std::vector<InterSecVector<Key_TYPE>>> rangeQueryRes;
                std::vector<std::vector<InterSecVector<Key_TYPE>>> rangeQueryRes_Temp;
                std::set<OID_TYPE> rangeAnsSet;
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
                time_begin = std::chrono::steady_clock::now();
                for (unsigned i = 0; i < PIVOT_NUM; ++i) {
//                    PerRangeQuery.clear();

//                    time_begin1 = std::chrono::steady_clock::now();
                    CalCirclePos<DIS_TYPE> refPtCircle(twoLayerIndex.upper.upperEntries[i].pivotPoint,
                                                       twoLayerIndex.upper.upperEntries[i].maxDistToPivot, queryPoint,
                                                       query_radius);

                    if (refPtCircle.label == 1) continue;

                    // 该条件不会执行：同心圆
                    if (refPtCircle.label == 3 &&
                        refPtCircle.dis_upper < twoLayerIndex.upper.upperEntries[i].minDistToPivot) {
                        continue;
                    }

                    if (refPtCircle.label == 3 && twoLayerIndex.upper.upperEntries[i].maxDistToPivot < query_radius) {
                        for (auto a: twoLayerIndex.upper.upperEntries[i].bottomEntryByMinDisToPivot) {
                            for (auto &object: a->objects) {
                                rangeAnsSet.insert(object.id);
                            }
                        }

                        continue;
                    }

//                    time_end1 = chrono::steady_clock::now();
//                    std::cout << "第一次查询圆与枢轴圆位置关系计算及非相交情况判断耗时: "
//                              << chrono::duration_cast<chrono::nanoseconds>(time_end1 - time_begin1).count() << " [ns]"
//                              << std::endl;


                    // 相交的情况
//                    time_begin1 = chrono::steady_clock::now();
                    twoLayerIndex.upperRangeQuery(queryPoint, query_radius, twoLayerIndex.upper.upperEntries[i],
                                                  PerRangeQuery, refPtCircle, rangeAnsSet, outFile);

//                    time_end1 = chrono::steady_clock::now();
//                    std::cout << "第一次查询圆与枢轴圆位置关系相交情况判断耗时: "
//                              << chrono::duration_cast<chrono::microseconds>(time_end1 - time_begin1).count() << " [µs]"
//                              << std::endl;


//                    std::sort(PerRangeQuery.begin(), PerRangeQuery.end(),
//                              [](const InterSecVector<Key_TYPE> &p1, const InterSecVector<Key_TYPE> &p2) {
//                                  return p1.oid < p2.oid;
//                              });

//                    rangeQueryRes.push_back(PerRangeQuery);
                }

                // 聚合上层结果
//                std::vector<InterSecVector<Key_TYPE>> aggResult = multiple_intersection(rangeQueryRes);

//                twoLayerIndex.bottomRangeQuery(aggResult, queryPoint, query_radius, rangeAnsSet, rangeAnsVector);
                time_end = chrono::steady_clock::now();
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
                outFile << "该轮 范围查询 查询结果数: " << rangeAnsSet.size() << "; 该轮 范围查询 耗时: "
                        << chrono::duration_cast<chrono::microseconds>(time_end - time_begin).count() << " [µs]"
                        << std::endl;
                outFile << "****************************************************************************" << std::endl;
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
                std::cout << "该轮 范围查询 查询结果数: " << rangeAnsSet.size() << "; 该轮 范围查询 耗时: "
                          << chrono::duration_cast<chrono::microseconds>(time_end - time_begin).count() << " [µs]"
                          << std::endl;
                std::cout << "************************************************************************" << std::endl;

                time += chrono::duration_cast<chrono::microseconds>(time_end - time_begin).count();
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
                for (auto &rangeQueryRe: rangeQueryRes) {
                    outFile << "*******************该轮枢轴点剪枝后获得的底层簇 ID **********************" << std::endl;
                    outFile << "总数:<" << rangeQueryRe.size() << ">" << std::endl;
                    for (const auto &element: rangeQueryRe) {
                        outFile << std::setw(10) << element.oid << "\t" << std::setw(10) << element.bottomEntry << "\t"
                                << std::setw(10) << element.bottomEntry->clusterCenter.id << std::endl;
                    }
                    outFile << "************************************************************" << std::endl << std::endl;
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//                    std::cout << "*****************该轮枢轴点剪枝后获得的底层簇 ID **********************" << std::endl;
//                    std::cout << "总数:<" << rangeQueryRe.size() << ">" << std::endl;
//                    for (const auto &element: rangeQueryRe) {
//                        std::cout << element.oid << " ";
//                    }
//                    std::cout << std::endl;
//
//                    std::cout << "*********************************************************" << std::endl << std::endl;
                }
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//                outFile << "*************************聚合后确定的底层簇 ID: ***************************" << std::endl;
//                for (auto &element: aggResult) {
//                    scanCluSet.insert(element.oid);
//                }
//                outFile << "总数:<" << scanCluSet.size() << ">" << std::endl;
//                for (auto &element: scanCluSet) {
//                    outFile << element << " ";
//                }
//                outFile << std::endl;
//                outFile << "****************************************************************" << std::endl << std::endl;
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//                std::cout << "*************************聚合后确定的底层簇 ID: ***************************" << std::endl;
//                std::cout << "总数:<" << scanCluSet.size() << ">" << std::endl;
//                for (auto &element: scanCluSet) {
//                    std::cout << element << " ";
//                }
//                std::cout << std::endl;
//                std::cout << "************************************************************************" << std::endl;
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
            }
            time /= static_cast<double>(list_rangeQry.size());

            outFile << "range query avg time is : " << time << " [µs]" << std::endl;
            std::cout << "range query avg time is : " << time << " [µs]" << std::endl;
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
            // knn query
//            std::vector<Point<Key_TYPE>> list_kNNQry;
//            query_filename = "../dataset/knn_points.txt";
////            query_filename = "../dataset/knn_forest.txt";
//            ReadDataFromFile_Query(query_filename, list_kNNQry);
//
//            int k = 5;
//            int lk = 100;
//            time = 0.0;
//            for (auto &queryPoint: list_kNNQry) {
//                std::chrono::steady_clock::time_point begin = std::chrono::steady_clock::now();
//                std::vector<OID_TYPE> knnResult = twoLayerIndex.kNNQuery(queryPoint, k, lk);
//                chrono::steady_clock::time_point end = chrono::steady_clock::now();
//                time += chrono::duration_cast<chrono::microseconds>(end - begin).count();
//                outFile << "kNN查询结果数量: " << knnResult.size() << std::endl;
//                for (auto &id: knnResult) {
//                    outFile << "查询结果中的数据点id: " << id << std::endl;
//                }
//                outFile << " kNN Query time is : " << chrono::duration_cast<chrono::microseconds>(end - begin).count()
//                        << " [µs]" << std::endl;
//            }
//            outFile << " kNN Query avg time is : " << time / static_cast<double>(list_kNNQry.size()) << " [µs]"
//                    << std::endl;
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

        }
        outFile << "**********************************************************************************" << std::endl;
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        outFile.close();
    } else {
        std::cerr << "无法打开文件 " << filename_main << " 进行写入。" << std::endl;
    }
    return 0;
}